package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.lkd.common.VMSystem;
import com.lkd.config.ConsulConfig;
import com.lkd.config.TopicConfig;
import com.lkd.contract.OrderCheck;
import com.lkd.contract.VendoutContract;
import com.lkd.contract.VendoutData;
import com.lkd.contract.VendoutResultContract;
import com.lkd.dao.OrderDao;
import com.lkd.feign.UserService;
import com.lkd.vo.*;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.OrderEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.VMService;
import com.lkd.service.OrderService;
import com.lkd.utils.DistributedLock;
import com.lkd.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    private VMService vmService;

    @Autowired
    private UserService userService;

    @Autowired
    private MqttProducer mqttProducer;

    @Autowired
    private ConsulConfig consulConfig;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public OrderEntity getByOrderNo(String orderNo) {
        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(OrderEntity::getOrderNo, orderNo);
        return this.getOne(qw);
    }


    /**
     * 创建订单
     *
     * @param vo
     * @return
     */
    @Override
    public OrderEntity createOrder(PayVO vo) {
        //1.判断库存--远程调用
        Boolean result = vmService.hasCapacity(vo.getInnerCode(), Long.valueOf(vo.getSkuId()));
        if (!result) {
            throw new LogicException("当前商品已售完");
        }

        //加分布式锁，售货机同一时间只能处理一次出货
        DistributedLock lock = new DistributedLock(
                consulConfig.getConsulRegisterHost(),
                consulConfig.getConsulRegisterPort()
        );
        DistributedLock.LockContext lockContext = lock.getLock(vo.getInnerCode(), 120);
        if (!lockContext.isGetLock()) {
            throw new LogicException("机器出货中，请稍后再试");
        }
        //为了能够释放锁，需要将sessionId存入redis
        redisTemplate.boundValueOps(VMSystem.VM_LOCK_KEY_PREF + vo.getInnerCode()) //绑定一个key的对象，我们可以通过这个对象来进行与key相关的对象
                .set(lockContext.getSession(), Duration.ofSeconds(120));

        System.err.println(lockContext.getSession() + "========================");

        //创建订单实体类
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOpenId(vo.getOpenId()); //微信用户openId
        orderEntity.setPayStatus(VMSystem.PAY_STATUS_NOPAY); //支付状态--未支付
        orderEntity.setOrderNo(vo.getInnerCode() + System.nanoTime()); //机器编号
        orderEntity.setPayType("2");

        //2.获取售货机信息
        VmVO vmInfo = vmService.getVMInfo(vo.getInnerCode());
        BeanUtils.copyProperties(vmInfo, orderEntity);
        orderEntity.setAddr(vmInfo.getNodeAddr());

        //3.获取商品信息
        SkuVO sku = vmService.getSku(vo.getSkuId());
        BeanUtils.copyProperties(sku, orderEntity);
        orderEntity.setAmount(sku.getRealPrice());
        orderEntity.setStatus(VMSystem.ORDER_STATUS_CREATE); //订单状态--创建

        //4.分成金额计算
        PartnerVO partner = userService.getPartner(vmInfo.getOwnerId());
        BigDecimal bg = new BigDecimal(sku.getRealPrice());
        int bill = bg.multiply(new BigDecimal(partner.getRatio()))
                .divide(new BigDecimal(100), 0, RoundingMode.HALF_UP)
                .intValue();
        orderEntity.setBill(bill);

        //5.调用保存方法
        save(orderEntity);

        //6.发送消息到emq--超时订单处理
        OrderCheck orderCheck = new OrderCheck();
        orderCheck.setOrderNo(orderEntity.getOrderNo());
        try {
            mqttProducer.send("$delayed/600/" + TopicConfig.ORDER_CHECK_TOPIC, 2, orderCheck);
        } catch (JsonProcessingException e) {
            log.error("订单消息发送到emq失败");
        }

        return orderEntity;
    }


    /**
     * 出货通知
     *
     * @param orderNo
     * @return
     */
    @Override
    public boolean vendOut(String orderNo) {
        //1.根据订单号查询订单
        OrderEntity orderEntity = getByOrderNo(orderNo);

        //2.更改订单状态
        orderEntity.setStatus(VMSystem.ORDER_STATUS_PAYED);
        orderEntity.setPayStatus(VMSystem.PAY_STATUS_PAYED);
        updateById(orderEntity);

        //3.发送消息
        //3.1封装协议
        VendoutContract vendoutContract = new VendoutContract();
        vendoutContract.setInnerCode(orderEntity.getInnerCode());
        VendoutData vendoutData = new VendoutData();
        vendoutData.setSkuId(orderEntity.getSkuId());
        vendoutData.setOrderNo(orderEntity.getOrderNo());

        vendoutContract.setVendoutData(vendoutData);
        //3.2发送消息
        try {
            mqttProducer.send(TopicConfig.VMS_VENDOUT_TOPIC, 2, vendoutContract);
        } catch (Exception e) {
            log.error("订单微服务发送出货协议失败", e);
            return false;
        }
        return true;
    }

    /**
     * 出货结果处理
     *
     * @param vendoutResultContract
     * @return
     */
    @Override
    public boolean vendoutResult(VendoutResultContract vendoutResultContract) {
        //查询出订单
        OrderEntity orderEntity = getByOrderNo(vendoutResultContract.getVendoutData().getOrderNo());
        if (orderEntity == null) {
            return false;
        }

        //1.出货失败
        if (!vendoutResultContract.isSuccess()) {
            log.info("售货机出货失败");
            orderEntity.setStatus(VMSystem.ORDER_STATUS_VENDOUT_FAIL);
            updateById(orderEntity);
            //todo: 发起退款
            return false;
        } else {
            //2.出货成功
            log.info("出货成功");
            orderEntity.setStatus(VMSystem.ORDER_STATUS_VENDOUT_SUCCESS);
            updateById(orderEntity);
            return true;
        }
    }
}
